package com.industry.framework.core.generic;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.TableFieldInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.industry.framework.common.redis.RedisUtils;
import com.industry.framework.core.cache.GlobleConstants;
import com.industry.framework.core.context.CoreApplicationContext;
import com.industry.framework.core.generic.record.Record;
import com.industry.framework.core.generic.record.RecordBuilder;
import com.industry.framework.core.generic.record.dataobject.dto.RecordDto;
import com.industry.framework.core.generic.record.dataobject.entity.RecordEntity;
import com.industry.framework.core.generic.mapper.RecordTempMapper;
import com.industry.framework.core.generic.record.RecordScanner;
import com.industry.framework.core.generic.table.TableConfigInfo;
import com.industry.framework.core.generic.table.TableConfigInfoBuilder;
import com.industry.framework.core.wrapper.RecordSearchWrapper;
import com.industry.framework.core.wrapper.UpdateWrapper;
import lombok.val;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.executor.BatchResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.ReflectionUtils;

import java.io.Serializable;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @Author nbZhuozh
 * @Date 2025-03-19  17:59
 **/
@Service
public abstract class AbstractCoreGenericService<T,ID> implements GenericService<T,ID>, Record<T> {
    private static final Logger log = LoggerFactory.getLogger(AbstractCoreGenericService.class);
    @Autowired
    private RecordTempMapper recordTempMapper;

    private final Class<T> entityClass;

    public Class<T> getEntityClass() {
        return entityClass;
    }
    @SuppressWarnings("unchecked")
    public AbstractCoreGenericService() {
        // 通过反射获取泛型类型
        Type type = this.getClass().getGenericSuperclass();
        ParameterizedType paramType = (ParameterizedType) type;
        Type[] actualTypeArguments = paramType.getActualTypeArguments();

        // 第一个泛型参数对应 T（实体类）
        this.entityClass = (Class<T>) actualTypeArguments[0];
    }
    //======================================= ↓ ↓ ↓  Generic CRUD ↓ ↓ ↓ ================================================
    /**
     * NOTES 需要指定Mapper，默认从容器中寻找 ，如果名称格式不规范，项目上service务必要重写该方法
     * @return
     */
    @Override
    public BaseMapper getMapper(){
        if(getEntityClass() == null){
            return  null;
        }
        return CoreApplicationContext.getGenericBaseMapper().get(getEntityClass().getSimpleName() + "Mapper");
    }
    @Override
    public T getDetails(ID id) {
        T t = (T) getMapper().selectById((Serializable) id);
        return t;
    }

    @Override
    public List<RecordDto> queryDetailsRecords(String id) {
        if(activeRecordBiz(getEntityClass())){
            //获取业务操作记录表名
            String recordTableName = RecordBuilder.findTargetTable(getEntityClass());
            return recordTempMapper.queryRecordsByDetail(recordTableName,id);
        }
        return null;
    }

    @Override
    public List<RecordDto> queryDetailsRecords(RecordSearchWrapper wrapper) {
        if(activeRecordBiz(getEntityClass())){
            //获取业务操作记录表名
            String recordTableName = RecordBuilder.findTargetTable(getEntityClass());
            if(StringUtils.isEmpty(wrapper.get_search_bizid())){
                return new ArrayList<>();
            }
            //没有填写内容 默认搜索全部
            if(StringUtils.isBlank(wrapper.get_search_bizid())){
               return queryDetailsRecords(wrapper.get_search_bizid());
            }
            return recordTempMapper.queryRecordsByDetailWrapper(recordTableName,wrapper.get_search_bizid(),wrapper.get_search_content());
        }
        return null;
    }

    @Override
    public void insertRecordRemark(String content , String bizId){
        if(!StringUtils.isAnyBlank(content,bizId)){
            //获取业务操作记录表名
            String recordTableName = RecordBuilder.findTargetTable(getEntityClass());
            //获取业务表名
            String bizTableName = getEntityClass().getAnnotation(TableName.class).value();
            doInsertRecordInfo(Collections.singletonList(RecordEntity.ofTypeRemark(bizTableName,bizId,content)),recordTableName);
        }
    }

    @Override
    public List<T> queryAll(){
        List<T> a = (List<T>) getMapper().selectList(new QueryWrapper<>());
        return a ;
    }
    @Override
    public List<? extends T> getTableList() {
        return null;
    }

    @Override
    public TableConfigInfo tableListQueryConfiger(){
        return TableConfigInfoBuilder.builder(getEntityClass());
    }


    @Override
    public void insertOrUpdate(List<T> items){
        /**
         * mybatisplus 执行逻辑不太适用，稍作修改：
         */
        //判断是否需要记录操作日志
        boolean isRecord = activeRecordBiz(getEntityClass());
        if(!isRecord){
            getMapper().insertOrUpdate(items);
            return;
        }
        TableInfo tableInfo = TableInfoHelper.getTableInfo(entityClass);
        String keyProperty = tableInfo.getKeyProperty();
        List<T> insertList = new ArrayList<>();
        List<T> updateList = new ArrayList<>();
        List<ID> updateIdList = new ArrayList<>();
        items.forEach(entity -> {
            Object keyVal = tableInfo.getPropertyValue(entity, keyProperty);
            if(keyVal == null){
                insertList.add(entity);
            }else{
                updateIdList.add((ID) keyVal);
                updateList.add(entity);
            }
        });
        List<UpdateWrapper<T>> updateWrapperList = new ArrayList<>();
        if(!CollectionUtils.isEmpty(updateList)){
            //历史数据结果（from database）
            List<T> infos = getMapper().selectList(new QueryWrapper<>().in(keyProperty, updateList));
            //新的数据结果（from web）
            Map<ID,T> infoMap = updateList.stream().collect(
                    Collectors.toMap(t -> (ID) tableInfo.getPropertyValue(t, keyProperty), Function.identity())
            );
            //根据items遍历出对象中新值、旧值 ， 构造wrapper
            infos.forEach(entity -> {
                UpdateWrapper<T> upwrp = new UpdateWrapper<>();
                UpdateWrapper.UpWrapperTemplate upWrapperTemplate = new UpdateWrapper.UpWrapperTemplate();
                Object oldKeyVal = tableInfo.getPropertyValue(entity, keyProperty);
                T newEntity = infoMap.get(String.valueOf(oldKeyVal));
                //遍历class 中 所有properties
                List<String> propertyList = new ArrayList<>();
                List<String> newValueList = new ArrayList<>();
                List<String> oldValueList = new ArrayList<>();
                for (TableFieldInfo field : tableInfo.getFieldList()) {
                    String property = field.getProperty();
                    Object oldValue = tableInfo.getPropertyValue(entity, property);
                    Object newValue = tableInfo.getPropertyValue(newEntity, property);
                    String oldStr = oldValue == null ? "" : oldValue.toString();
                    String newStr = newValue == null ? "" : newValue.toString();
                    if (!StringUtils.equals(oldStr, newStr)) {
                        propertyList.add(property);
                        newValueList.add(newStr);
                        oldValueList.add(oldStr);
                    }
                }
                if(!CollectionUtils.isEmpty(propertyList)){
                    upWrapperTemplate.set_up_field(propertyList.toArray(new String[propertyList.size()]));
                    upWrapperTemplate.set_up_n(newValueList.toArray(new String[newValueList.size()]));
                    upWrapperTemplate.set_up_o(oldValueList.toArray(new String[oldValueList.size()]));
                }
                upwrp.setU_data(entity);
                upwrp.set_up_wrapper(upWrapperTemplate);
                updateWrapperList.add(upwrp);
            });
        }
        //这两个方法里面有对应搞record 的地方
        batchInsert(insertList);
        for (UpdateWrapper<T> tUpdateWrapper : updateWrapperList) {
            batchUpdate(tUpdateWrapper);
        }
        return;
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void delIds(List<ID> delIds) {
        if(!CollectionUtils.isEmpty(delIds)){
            getMapper().deleteByIds(delIds);
        }
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public List<String> batchInsert(List<T> list) {
        getMapper().insert(list, 500);
        List<String> resultIds = new ArrayList<>();
        list.forEach(i->{
            Method method = ReflectionUtils.findMethod(getEntityClass(), "getId");
            if(method != null){
                String idValue =String.valueOf( ReflectionUtils.invokeMethod(method, i));
                resultIds.add(idValue);
            }
        });
        //判断当前业务类是否需要记录
        if(activeRecordBiz(getEntityClass())){
            doMarkRecordTypeInsert(resultIds);
        }
        return resultIds;
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void batchUpdate(List<T> list) {
        getMapper().updateById(list,200);
    }


    @Transactional(propagation = Propagation.REQUIRED , rollbackFor = Exception.class)
    public void batchUpdate(UpdateWrapper<T> wrapper){
        if(!ObjectUtils.isEmpty(wrapper.getU_data())){
            List<T> updatorList = Collections.singletonList(wrapper.getU_data());
            List<BatchResult> list = getMapper().updateById(updatorList);
            if(activeRecordBiz(getEntityClass())){
                //记录操作记录
                list.forEach(i->{
                    markRecordTypeUpdate(wrapper.getU_data(),i, wrapper.get_up_wrapper());
                });
            }
        }
    }
    //======================================= ↑ ↑ ↑  Generic CRUD ↑ ↑ ↑ ================================================

    //==============================================BASE RECORD SERVICE START===========================================

    /**
     * 适用已知的Class，及新增返回的id，例如当 A extends GenericService  可自定义使用如下方法 记录操作记录
     */
    public void doMarkRecordTypeInsert(List<String> insertIds){
        List<RecordEntity> insertList = new ArrayList<>();
        //获取业务操作记录表名
        String recordTableName = RecordBuilder.findTargetTable(getEntityClass());
        //获取业务表名
        String bizTableName = getEntityClass().getAnnotation(TableName.class).value();
        for (String id : insertIds) {
            if(!StringUtils.isEmpty(id)){
                //构造record实体 , 新增操作 只需要 业务表 和业务Id即可
                insertList.add(RecordEntity.ofTypeInsert(bizTableName, id));
            }
        }
        doInsertRecordInfo(insertList,recordTableName);
    }

    /**
     * 已知原数据，修改后result ，修改后字段
     * @param source
     * @param result
     * @param wrapper
     */
    @Override
    public void markRecordTypeUpdate(T source , BatchResult result , UpdateWrapper.UpWrapperTemplate wrapper){
        if(source == null || result == null){
            return;
        }
        IdVisitor vs = IdVisitor.build(getEntityClass());
        vs.visit(result,source);
        if(StringUtils.isEmpty(vs.getId())){
            return;
        }
        markRecordTypeUpdate(vs.getId(),wrapper.get_up_field(),wrapper.get_up_o(),wrapper.get_up_n());
    }

    /**
     * 已知修改的业务表id 及字段
     * @param bizId
     * @param wrapper
     */
    @Override
    public void markRecordTypeUpdate(String bizId, UpdateWrapper.UpWrapperTemplate wrapper){
        if(StringUtils.isEmpty(bizId)){
            return;
        }
        markRecordTypeUpdate(bizId,wrapper.get_up_field(),wrapper.get_up_o(),wrapper.get_up_n());
    }

    /**
     * 更新操作的数据
     * @param
     */
    @Override
    public void markRecordTypeUpdate(String id, String[] updateFields  , String[] oldVal , String[] newVal ){
        //todo
        int length = updateFields.length;
        if(length != oldVal.length || length != newVal.length){
            return;
        }
        List<RecordEntity> insertList = new ArrayList<>();
        //获取业务操作记录表名
        String recordTableName = RecordBuilder.findTargetTable(getEntityClass());
        //获取业务表名
        String bizTableName = getEntityClass().getAnnotation(TableName.class).value();
        if(!StringUtils.isEmpty(id)){
            int turn = 0 ;
            do {
                //判断当前数据是否需要从码表中获取
                RecordScanner.RecordPropertyFields propertyField = RecordScanner.findPropertyField(getEntityClass(), updateFields[turn]);
                if(propertyField != null){
                    //取码值
                    if(!StringUtils.isEmpty(propertyField.getCodeKey())){
                        //todo redis 获取码值 更新 newVal[turn] 与 oldVal[turn]
                        List<String> res =
                                JSON.parseArray((String) RedisUtils.getCacheMap(GlobleConstants.SYSTEM_CODE).get(propertyField.getCodeKey()),String.class);
                        if(!CollectionUtils.isEmpty(res)){
                            int finalTurn = turn;
                            res.forEach(i->{
                                ObjectMapper o = new ObjectMapper();
                                try {
                                    Map<String, Object> a = o.readValue(i, new TypeReference<Map<String, Object>>() {});
                                    if(StringUtils.equals(String.valueOf(a.get("bizValue")),newVal[finalTurn])){
                                        newVal[finalTurn] = String.valueOf(a.get("bizName"));
                                    }
                                    if(StringUtils.equals(String.valueOf(a.get("bizValue")),oldVal[finalTurn])){
                                        oldVal[finalTurn] = String.valueOf(a.get("bizName"));
                                    }
                                } catch (JsonProcessingException e) {
                                    e.printStackTrace();
                                }
                            });
                        }
                    }
                    insertList.add(RecordEntity.ofTypeUpdate(bizTableName,id,
                            updateFields[turn],propertyField.getTemplateName(),newVal[turn],oldVal[turn]));
                }
                turn ++;
            } while (turn < length);
        }
        doInsertRecordInfo(insertList,recordTableName);
    }

    @Transactional(propagation = Propagation.MANDATORY , rollbackFor = Exception.class) //必须存在事务
    public void doInsertRecordInfo(List<RecordEntity> insert , String targetRecordTableName){
        if(!CollectionUtils.isEmpty(insert)){
            recordTempMapper.insertBatch(targetRecordTableName,insert);
        }
    }
    //==============================================BASE RECORD SERVICE END=============================================

    static class IdVisitor{
        private String id = "";
        private final Class<?> targetType;
        IdVisitor(Class<?> targetType) {
            this.targetType = targetType;
        }
        public static IdVisitor build(Class<?> targetType){
            return new IdVisitor(targetType);
        }
        public void visit(BatchResult result , Object orginSource){
            int[] updateCounts = result.getUpdateCounts(); //mybatisplus实际影响行数，例如update 搜索到了两条数据，第一条更新成功，第二条未更新到，此时返回int[1,0];
            //所以在这里，仅处理updateById的相关业务，因为updateById 永远只会 int[] .leng  = 1；根据第一个判断是否成功即可。
            if(updateCounts.length>0 && updateCounts[0] == 1){
                //这里再上源数据去找对应的业务id ， 也是get(0)即可。
                Object o = result.getParameterObjects().get(0);
                Method method = ReflectionUtils.findMethod(targetType, "getId");
                if(method != null && orginSource != null){
                    Object idValue = ReflectionUtils.invokeMethod(method, orginSource);
                    if(!ObjectUtils.isEmpty(idValue)){
                        this.id = String.valueOf(idValue);
                    }
                }
            }
        }
        public String getId(){
            return id;
        }
    }
}
